package com.zb.util.web;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import com.zb.util.CommonSettingsUtil;
import com.zb.util.JsonUtils;

/**
 * HTTP工具类，简化http请求处理
 * 
 * 作者: zhoubang 日期：2015年3月26日 下午1:31:05
 */
public class HttpUtils {

    private static String DEFAULT_CHARSET;
    private static int DEFAULT_TIMEOUT;
    private static String PROXY_HOST;
    private static int PROXY_PORT;

    static {
        DEFAULT_CHARSET = CommonSettingsUtil.getInstance().getDefaultCharset();
        DEFAULT_TIMEOUT = CommonSettingsUtil.getInstance().getDefaultHttpTimeout();
        PROXY_HOST = CommonSettingsUtil.getInstance().getDefaultHttpHost();
        PROXY_PORT = CommonSettingsUtil.getInstance().getDefaultHttpProxyPort();
    }

    /**
     * 获取JSON，默认GET方法，默认编码UTF-8
     * 
     * @param url
     * @return
     * @throws Exception
     */
    public static final String getJson(String url) throws Exception {
        return getJson(url, HttpMethod.GET);
    }

    /**
     * 获取json,默认编码UTF-8
     * 
     * @param url
     * @param method
     * @return
     * @throws Exception
     */
    public static final String getJson(String url, HttpMethod method)
            throws Exception {
        return getJson(url, method, null);
    }

    /**
     * 获取json
     * 
     * @param url
     * @param method
     * @param params
     * @return
     * @throws Exception
     */
    public static final String getJson(String url, HttpMethod method,
            Map<String, String> params) throws Exception {
        return getJson(url, method, params, DEFAULT_CHARSET);
    }

    /**
     * 获取json
     * 
     * @param url
     * @param method
     * @param params
     * @param encode
     * @return
     * @throws Exception
     */
    public static final String getJson(String url, HttpMethod method,
            Map<String, String> params, String encode) throws Exception {
        return getJson(url, method, params, encode, DEFAULT_TIMEOUT);
    }

    /**
     * 获取json
     * 
     * @param url
     * @param method
     * @param params
     * @param encode
     * @param timeoutMills
     * @return
     * @throws Exception
     */
    public static final String getJson(String url, HttpMethod method,
            Map<String, String> params, String encode, int timeoutMills)
            throws Exception {
        return getJson(url, method, params, encode, timeoutMills, null, -1);
    }

    /**
     * 获取json
     * 
     * @param url
     * @param method
     * @param params
     * @param encode
     * @param timeoutMills
     * @param proxyHost
     * @param proxyPort
     * @return
     * @throws Exception
     */
    public static final String getJson(String url, HttpMethod method,
            Map<String, String> params, String encode, int timeoutMills,
            String proxyHost, int proxyPort) throws Exception {
        String jsonStr = getString(url, method, params, encode, timeoutMills, proxyHost, proxyPort);
        try {
            JsonUtils.fromJson(jsonStr);
            return jsonStr;
        } catch (Throwable e) {
            throw new Exception("string is not json: " + jsonStr);
        }
    }

    /**
     * 获取实体类
     * 
     * @param url
     * @param clazz
     * @return
     * @throws Exception
     */
    public static final <T> T getEntity(String url, Class<T> clazz)
            throws Exception {
        return getEntity(url, HttpMethod.GET, clazz);
    }

    /**
     * 获取实体类
     * 
     * @param url
     * @param method
     * @param clazz
     * @return
     * @throws Exception
     */
    public static final <T> T getEntity(String url, HttpMethod method,
            Class<T> clazz) throws Exception {
        return getEntity(url, method, clazz, null);
    }

    /**
     * 获取实体类
     * 
     * @param url
     * @param method
     * @param clazz
     * @param params
     * @return
     * @throws Exception
     */
    public static final <T> T getEntity(String url, HttpMethod method,
            Class<T> clazz, Map<String, String> params) throws Exception {
        return getEntity(url, method, clazz, params, DEFAULT_CHARSET);
    }

    /**
     * 获取实体类
     * 
     * @param url
     * @param method
     * @param clazz
     * @param params
     * @param encode
     * @return
     * @throws Exception
     */
    public static final <T> T getEntity(String url, HttpMethod method,
            Class<T> clazz, Map<String, String> params, String encode)
            throws Exception {
        return getEntity(url, method, clazz, params, encode, DEFAULT_TIMEOUT);
    }

    /**
     * 获取实体类
     * 
     * @param url
     * @param method
     * @param clazz
     * @param params
     * @param encode
     * @param timeoutMills
     * @return
     * @throws Exception
     */
    public static final <T> T getEntity(String url, HttpMethod method, Class<T> clazz, Map<String, String> params, String encode, int timeoutMills) throws Exception {
        return getEntity(url, method, clazz, params, encode, timeoutMills, null, -1);
    }

    /**
     * 获取实体类
     * 
     * @param url
     * @param method
     * @param clazz
     * @param params
     * @param encode
     * @param timeoutMills
     * @param proxyHost
     * @param proxyPort
     * @return
     * @throws Exception
     */
    public static final <T> T getEntity(String url, HttpMethod method,
            Class<T> clazz, Map<String, String> params, String encode,
            int timeoutMills, String proxyHost, int proxyPort) throws Exception {
        String json = getString(url, method, params, encode, timeoutMills, proxyHost, proxyPort);
        return JsonUtils.fromJson(json, clazz);
    }

    /**
     * 获取字符串
     * 
     * @param url
     * @return
     * @throws Exception
     */
    public static final String getString(String url) throws Exception {
        return getString(url, HttpMethod.GET);
    }

    /**
     * 获取字符串
     * 
     * @param url
     * @param method
     * @return
     * @throws Exception
     */
    public static final String getString(String url, HttpMethod method)
            throws Exception {
        return getString(url, method, null);
    }

    /**
     * 获取字符串
     * 
     * @param url
     * @param method
     * @param params
     * @return
     * @throws Exception
     */
    public static final String getString(String url, HttpMethod method,
            Map<String, String> params) throws Exception {
        return getString(url, method, params, DEFAULT_CHARSET);
    }

    /**
     * 获取字符串
     * 
     * @param url
     * @param method
     * @param params
     * @param encode
     * @return
     * @throws Exception
     */
    public static final String getString(String url, HttpMethod method,
            Map<String, String> params, String encode) throws Exception {
        return getString(url, method, params, encode, DEFAULT_TIMEOUT);
    }

    /**
     * 获取字符串
     * 
     * @param url
     * @param method
     * @param params
     * @param encode
     * @param timeoutMills
     * @return
     * @throws Exception
     */
    public static final String getString(String url, HttpMethod method,
            Map<String, String> params, String encode, int timeoutMills)
            throws Exception {
        return getString(url, method, params, encode, timeoutMills, null, -1);
    }

    /**
     * 获取字符串
     * 
     * @param url
     * @param method
     * @param params
     * @param encode
     * @param timeoutMills
     * @param proxyHost
     * @param proxyPort
     * @return
     * @throws Exception
     */
    public static final String getString(String url, HttpMethod method,
            Map<String, String> params, String encode, int timeoutMills, String proxyHost, int proxyPort) throws Exception {
        CloseableHttpClient httpclient = HttpClients.createDefault();
        CloseableHttpResponse resp = null;

        if (HttpMethod.GET.equals(method)) {
            String foo = generateGetUrl(url, params);
            HttpGet httpget = new HttpGet(foo);
            httpget.setConfig(getRequestConfig(timeoutMills, proxyHost, proxyPort));
            try {
                resp = httpclient.execute(httpget);
            } catch (IOException e) {
                e.printStackTrace();
                throw new Exception("http client execute error: [url=" + foo + ",method=get]", e);

            }
        } else {
            HttpPost httppost = new HttpPost(url);
            httppost.setConfig(getRequestConfig(timeoutMills, proxyHost, proxyPort));
            List<NameValuePair> nvps = new ArrayList<NameValuePair>();
            if (params != null) {
                for (Iterator<Map.Entry<String, String>> it = params.entrySet().iterator(); it.hasNext();) {
                    Map.Entry<String, String> entry = it.next();
                    nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
                }
            }
            try {
                // httppost.setEntity(new UrlEncodedFormEntity(nvps));
                httppost.setEntity(new UrlEncodedFormEntity(nvps, encode));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace(); // To change body of catch statement use
                                     // File | Settings | File Templates.
                throw new Exception(e);
            }
            try {
                resp = httpclient.execute(httppost);
            } catch (IOException e) {
                e.printStackTrace(); // To change body of catch statement use
                                     // File | Settings | File Templates.
                throw new Exception("http client execute error: [url=" + url + ",method=post,params=" + JsonUtils.toJson(params) + "]", e);
            }
        }

        InputStream in = null;
        String result = null;
        try {
            StatusLine line = resp.getStatusLine();
            if (line.getStatusCode() != HttpStatus.SC_OK) {
                throw new Exception("http client response status ERROR: " + line.toString());
            }
            HttpEntity entity1 = resp.getEntity();
            in = entity1.getContent();
            result = IOUtils.toString(in, encode);
            EntityUtils.consume(entity1);
        } catch (IOException e) {
            e.printStackTrace(); // To change body of catch statement use File |
                                 // Settings | File Templates.
            throw new Exception(e);
        } finally {
            IOUtils.closeQuietly(in);
            IOUtils.closeQuietly(resp);
        }
        return result;
    }

    /**
     * 获取请求设置对象
     * 
     * @param timeoutMills
     * @param proxyHost
     * @param proxyPort
     * @return
     */
    private static final RequestConfig getRequestConfig(int timeoutMills,
            String proxyHost, int proxyPort) {
        RequestConfig defaultRequestConfig = RequestConfig.custom()
                .setCookieSpec(CookieSpecs.BEST_MATCH)
                .setExpectContinueEnabled(true)
                .setStaleConnectionCheckEnabled(true)
                .setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM, AuthSchemes.DIGEST))
                .setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC))
                .build();

        RequestConfig requestConfig = null;
        final Builder requestConfigBuilder = RequestConfig
                .copy(defaultRequestConfig).setSocketTimeout(timeoutMills)
                .setConnectTimeout(timeoutMills)
                .setConnectionRequestTimeout(timeoutMills);
        if (StringUtils.isNotBlank(proxyHost) && proxyPort > 0) {
            requestConfigBuilder.setProxy(new HttpHost(proxyHost, proxyPort));
        } else if (StringUtils.isNotBlank(PROXY_HOST) && PROXY_PORT > 0) {
            requestConfigBuilder.setProxy(new HttpHost(PROXY_HOST, PROXY_PORT));
        }
        requestConfig = requestConfigBuilder.build();
        return requestConfig;
    }

    private static final String generateGetUrl(String url, Map<String, String> params) {
        StringBuffer sb = new StringBuffer(url).append("?");
        if (params != null) {
            for (Iterator<Map.Entry<String, String>> it = params.entrySet().iterator(); it.hasNext();) {
                Map.Entry<String, String> entry = it.next();
                sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
        }
        String foo = sb.toString();
        foo = foo.substring(0, foo.length() - 1);
        return foo;
    }

}
